package com.nercel.dsj.gksales.shiro.filter;

import com.alibaba.fastjson.JSON;
import com.github.maltalex.ineter.base.IPv4Address;
import com.github.maltalex.ineter.range.IPv4Range;
import com.nercel.dsj.gksales.config.ApplicationContextHolder;
import com.nercel.dsj.gksales.constant.Conf;
import com.nercel.dsj.gksales.model.ResponseResult;
import com.nercel.dsj.gksales.service.ConfigurationService;
import com.nercel.dsj.gksales.shiro.util.ShiroUtil;
import com.nercel.dsj.gksales.util.IpUtil;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author sunlin
 * @date 2018-11-20
 */
@Component
public class RestPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {

    private static final Logger logger = LoggerFactory.getLogger(RestPermissionsAuthorizationFilter.class);

    @Override
    protected boolean pathsMatch(String path, ServletRequest request) {
        String realPath = ShiroUtil.getRealPath(path, request);
        return realPath != null && super.pathsMatch(realPath, request);
    }

    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        Subject subject = this.getSubject(request, response);
        return authIpWhiteListCheck(request)
                || subject.hasRole("SUPER_ADMIN")
                || isLoginAttempt(request)
                && super.isAccessAllowed(request, response, mappedValue);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
        String respData = JSON.toJSONString(new ResponseResult(null, "UNAUTHORIZED", null));
        httpResponse.getWriter().write(respData);
        httpResponse.getWriter().flush();
        return false;
    }

    private boolean isLoginAttempt(ServletRequest request) {
        HttpServletRequest req = (HttpServletRequest) request;
        String authorization = req.getHeader("Authorization");
        return authorization != null;
    }

    private boolean authIpWhiteListCheck(ServletRequest request){
        String ip = IpUtil.getIpAddr((HttpServletRequest)request);
        logger.debug("client ip is : " + ip);
        IPv4Address ipv4;
        try {
            ipv4 = IPv4Address.of(ip);
        }catch (Exception e){
            return false;
        }
        List<String> ipList = getIpList();
        for(String ipRange : ipList){
            IPv4Range ipv4Range = IPv4Range.between(ipRange);
            if(ipv4Range.contains(ipv4)){
                return true;
            }
        }
        return false;
    }

    private List<String> getIpList(){
        List<String> result = new ArrayList<>();
        ConfigurationService configurationService = ApplicationContextHolder.getApplicationContext().getBean(ConfigurationService.class);
        String authIpWhiteList = configurationService.getAsString(Conf.AUTH_IP_WHITE_LIST);
        Pattern pattern = Pattern.compile("\\[((2(5[0-5]{1}|[0-4]\\d{1})|[0-1]?\\d{1,2})(\\.(2(5[0-5]{1}|[0-4]\\d{1})|[0-1]?\\d{1,2})){3}" +
                "\\-(2(5[0-5]{1}|[0-4]\\d{1})|[0-1]?\\d{1,2})(\\.(2(5[0-5]{1}|[0-4]\\d{1})|[0-1]?\\d{1,2})){3})\\]");
        Matcher matcher = pattern.matcher(authIpWhiteList);
        while (matcher.find()) {
            result.add(matcher.group(1));
        }
        return result;
    }



}
